package org.tulkas.l10n;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tulkas.base.Constants;

/**
 * @author <a href="mailto:tulkas.web@gmail.com">Tulkas</a>
 *
 */
public class TulkasResourceBundle extends ResourceBundle {
	private static Logger logger = LoggerFactory
			.getLogger(TulkasResourceBundle.class);

	public static final String LANGUAGES_DIR = "languages";
	public static final String LANGUAGES_SUFFIX = "properties";

	private Map<String, Object> lookup = new HashMap<String, Object>();

	public TulkasResourceBundle(String baseName, Locale locale,
			ClassLoader loader, ResourceBundleControl control) {
		String fileName = control.toResourceName(control.toBundleName(baseName,
				locale), LANGUAGES_SUFFIX);
		try {
			Enumeration<URL> urls = loader.getResources(LANGUAGES_DIR + "/"
					+ fileName);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				InputStream is = url.openStream();
				try {
					loadMessages(is);
				} catch (IOException e) {
					logger.error(
							"Error loading resource bundle file from classpath ["
									+ url.toString() + "]", e);
				} finally {
					is.close();
				}
			}

			List<File> files = searchFileFromWebRoot(fileName, control);
			for (File file : files) {
				FileInputStream fis = new FileInputStream(file);
				try {
					loadMessages(fis);
				} catch (IOException e) {
					logger.error("Error loading resource bundle file ["
							+ file.toString() + "]", e);
				} finally {
					fis.close();
				}
			}
		} catch (IOException e) {
			logger.error("Error initializing resource bundle [baseName: "
					+ baseName + ", locale: " + locale.toString() + "]", e);
		}
	}

	private List<File> searchFileFromWebRoot(String fileName,
			ResourceBundleControl control) {
		List<File> list = new ArrayList<File>();
		String pluginDirStr = control.getWebRootPath() + "/"
				+ Constants.PLUGINS_DIR;
		File pluginDir = new File(pluginDirStr);
		if (pluginDir.exists()) {
			File[] dirs = pluginDir.listFiles();
			for (int i = 0; i < dirs.length; i++) {
				if (dirs[i].isDirectory()) {
					File file = new File(dirs[i], LANGUAGES_DIR + "/"
							+ fileName);
					if (file.exists()) {
						list.add(file);
					}
				}
			}
		}

		return list;
	}

	private void loadMessages(InputStream is) throws IOException {
		Properties props = new Properties();
		props.load(is);
		Iterator<Entry<Object, Object>> it = props.entrySet().iterator();
		while (it.hasNext()) {
			Entry<Object, Object> entry = it.next();
			lookup.put((String) entry.getKey(), entry.getValue());
		}
	}

	@Override
	public Enumeration<String> getKeys() {
		return new ResourceBundleEnumeration(lookup.keySet(),
				(parent != null) ? parent.getKeys() : null);
	}

	@Override
	protected Object handleGetObject(String key) {
		return lookup.get(key);
	}
}

class ResourceBundleEnumeration implements Enumeration<String> {

	Set<String> set;
	Iterator<String> iterator;
	Enumeration<String> enumeration; // may remain null

	/**
	 * Constructs a resource bundle enumeration.
	 * @param set an set providing some elements of the enumeration
	 * @param enumeration an enumeration providing more elements of the enumeration.
	 *        enumeration may be null.
	 */
	ResourceBundleEnumeration(Set<String> set, Enumeration<String> enumeration) {
		this.set = set;
		this.iterator = set.iterator();
		this.enumeration = enumeration;
	}

	String next = null;

	public boolean hasMoreElements() {
		if (next == null) {
			if (iterator.hasNext()) {
				next = iterator.next();
			} else if (enumeration != null) {
				while (next == null && enumeration.hasMoreElements()) {
					next = enumeration.nextElement();
					if (set.contains(next)) {
						next = null;
					}
				}
			}
		}
		return next != null;
	}

	public String nextElement() {
		if (hasMoreElements()) {
			String result = next;
			next = null;
			return result;
		} else {
			throw new NoSuchElementException();
		}
	}
}
